Skip to content

test(runner): pin the suite's timezone to UTC - #8486

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-8366-date-face-timezone
Sep 8, 2026
Merged

test(runner): pin the suite's timezone to UTC#8486
baozhoutao merged 2 commits into
mainfrom
claude/issue-8366-date-face-timezone

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #8366

The defect, re-measured on origin/main (76573a184)

The date-face pin family asserts literal local-date faces built from fixed
UTC instants through local date parts. Nothing pinned the runner's zone, so
the face those pins render was a property of the contributor's laptop.

All six files the card names, run together, one run per zone:

TZ offset Test Files Tests
UTC +00:00 6 passed 184 passed
Europe/Paris +02:00 3 failed / 3 passed 7 failed
Asia/Shanghai +08:00 3 failed / 3 passed 7 failed
America/New_York −04:00 4 failed / 2 passed 31 failed
Etc/GMT+8 −08:00 4 failed / 2 passed 33 failed

Two corrections to the card

  1. The card says "red for any contributor west of UTC-07:00". Measurement
    says the suite is green at EXACTLY ONE offset.
    It is red at Europe/Paris
    and Asia/Shanghai too — east of UTC, not west of anything. A face like
    Jul 4, 2024, 07:00 AM, pinned off an 07:00Z instant, is true at UTC+00:00
    and nowhere else. Sample failures: expected 'Jul 3, 2024, 11:00 PM' to be 'Jul 4, 2024, 07:00 AM', expected '7/3/2024 11:00 pm' to be '7/4/2024 7:00 am'.
  2. The card's single-file reading has moved. It reported 10 failed | 4 passed (14)
    at base 411a132c0; on 76573a184 the same file reads 11 failed | 4 passed (15)
    — PR test(core): pin the OUT-of-window precedence pair formatDate(V, 'relative', { style: 'short' }) #8353 added a row. The defect is unchanged; only the count is.

The card's zeros reproduce exactly, each against a lit control:
TZ / timeZone = 0 in vitest.config.mts, turbo.json, package.json and
all six vitest.setup.*; TZ: = 0 workflows. Controls: 'environment' in
vitest.config.mts = 8; runs-on = 34 workflows.

The asymmetry the card asked to have explained

dataset-format.date.test.ts and DatasetWidget.dateMeasure.test.tsx pass in
every zone, and it is not luck. Both derive the expected face by calling the
same formatter the code under test calls, so the zone cancels:

expect(out).toBe(formatDateTime(ISO_DATETIME, { locale: EN }));

DatasetWidget.dateMeasure.test.tsx says so in a docblock headed "Why the
expectations are DERIVED and not literal"
— and it was chosen for a different
reason entirely (a literal would pass just as well against a second date
convention, the objectui#4576 failure its ruling forbids).

Why the zone and not the assertions — the fork, argued

The four failing files pin literals on purpose, and
date-display.optionsStyle-7745.test.ts states the purpose in prose: its faces
are "anchored by the two literals the card measured, so a redesign that moved
both sides together could not pass silently."
Those literals sit next to
equivalence assertions of the form
formatDate(V, undefined, { style }) === formatDate(V, style), which are
zone-independent already and which a redesign could move on both sides at once.
The literals are the anti-tautology anchor.

So rewriting them to derive the face is not a refactor — it turns the anchor
into formatDate(v) === formatDate(v) and deletes the thing the file was
written for. This repo already uses each form where it belongs; the fix should
not flatten them into one.

Chosen: pin the runner's zone. One line, in the one place that already owns
how this repo runs tests:

process.env.TZ = 'UTC';

apps/console/vitest.config.ts imports the root config, so its module scope runs
for that project too; the 11 remaining packages/*/vite.config.ts reach
assertCanonicalVitestInvocation, which refuses the package-cwd invocations that
could bypass it. Measured: with the pin, all six files are 184 passed under
Etc/GMT+8, America/New_York, Asia/Shanghai and UTC.

Unconditional, not ??=. A contributor's zone usually comes from
/etc/localtime, not from TZ, so a conditional assignment would leave exactly
the reported population unfixed while looking like a fix. The guard test forbids
the ??= / ||= spelling for that reason.

It deletes no coverage — measured, not assumed

The card asks whether pinning hides a real zone bug. It does not:

  • Zero tests in the repo read the ambient zone — process.env.TZ,
    getTimezoneOffset, Intl.DateTimeFormat().resolvedOptions() return 0 hits
    across every *.test.ts / *.test.tsx. Live control: the same
    resolvedOptions() pattern returns 5 hits elsewhere in the tree, so the grep
    fires.
  • The one zone-aware production surface, GanttView.tsx's makeTzShift /
    tzOffsetMs, takes its zone from metadata (schema.timeZone), and
    GanttView.tzshift.test.ts already tests it with explicit per-case zones
    (Asia/Shanghai, America/New_York, a DST pair). That is exactly the shape
    the card says non-UTC coverage should have, and it is unaffected.

Costs of the options not taken

  • B — rewrite the pins. Removes the same class, at roughly 50x the diff, and
    pays for it by converting four files' deliberate literal anchors into
    self-comparisons. fields-date-widget-convention-8194.test.tsx alone carries
    16 failing cases whose expected faces would become tautologies.
  • C — both, scoped. Its incremental half over A is an inventory of "pins
    that would still be zone-dependent". That document has no consumer and no
    gate; the useful part of it is the guard below plus the asymmetry explanation
    above, both of which ship here.

The pin is pinned — scripts/__tests__/vitest-timezone-pin-8366.test.ts

CI runs in UTC. So an in-process getTimezoneOffset() === 0 would be green on CI
whether or not the config still pins anything — it would catch a deleted pin only
for the contributor it protects, and stay silent where merges are gated. That is
the original defect one level up.

So the guard spawns a real vitest with TZ=Etc/GMT+8 and asserts the run
still sees UTC. Four cases: the ambient reading; a node -e live control proving
TZ actually reaches a child (without it, a broken env hand-off would make the
spawn pass forever); the spawn itself; and a static half that names the file and
forbids the conditional spelling.

Ablation — direction predicted in writing before the run

Predicted: the spawn case and the static case go RED; the ambient case stays
GREEN (this box is UTC — which is the point), and the control stays green.

Mutation proven on disk before reading any result:

HEAD blob for vitest.config.mts = 89896da7cd4777abfb6bab7f66c04b1691600ed1
MARKER before: 1        MARKER after (expect 0): 0
INJECTED marker (expect 1): 1
on-disk blob now = 04beeb3449fc516137e3c824f98728b6647df7f8
MUTATION PROVEN ON DISK

Observed, matching the prediction:

 × a real vitest spawned under a non-UTC TZ still runs in UTC   1592ms
 × the pin is where the spawn says it is                           3ms
 Test Files  1 failed (1)
      Tests  2 failed | 2 passed (4)

and inside the spawned child, visible in the failure output — the direct reading
that the pin is what forces UTC:

+      × the suite runs in UTC, whatever zone the contributor is in
+ AssertionError: expected 480 to be +0

Restored under an EXIT INT TERM trap with absolute paths, via
git checkout HEAD -- ..., and proven by state: git diff HEAD --stat
printed nothing.

Files touched outside scripts/

Per the card's scope fence — this fix touches no other lane's test file.
The four red files are fixed by the runner, not edited.

  • vitest.config.mts — the pin plus its comment. Contents only: the
    check-lint-rule-coverage UNREACHED_GROUPS row that names this exact path
    (objectui#8337, PR tooling(devx): report source files ESLint reaches for NEITHER reason #8467) is untouched, and check:lint-rule-coverage is green.
    Independently confirmed by measurement: the file appears 0 times in
    lint:root's 276-file population, which is what that row asserts.
  • .changeset/8366-vitest-timezone-pin.md — empty frontmatter. Test
    infrastructure; no package is released by this change.

Verification

what result
the six files x 5 zones, before UTC green; 7 / 7 / 31 / 33 failures at +02:00 / +08:00 / −04:00 / −08:00
the six files x 4 zones, after 184 passed in every one
pnpm exec vitest run scripts/ 122 passed (122), 3634 passed (3634) — includes every test that reads vitest.config.mts
ablation 2 failed | 2 passed (4), matching the written prediction; restore proven by git diff HEAD empty
node scripts/check-changeset-presence.mjs exit 0 — "no changeset is owed"; one added anyway, declaring no release
node scripts/check-lint-rule-coverage.mjs exit 0
node scripts/check-governed-queue-guard.mjs --test ... exit 0 — NOT GOVERNED, 3 paths vs 5 governed surfaces
check:control-bytes · check:vi-mock-inherit · check:vi-mock-specifiers · check:unreferenced-sources exit 0
type-check:scripts · type-check:coverage · type-check:vitest-config exit 0

Lint, as a proven narrowing (at e1641f8e6, the final commit):
lint:root — the repo's own root-lane invocation, so the receiving population is
read from ESLint's own config, not guessed — judged 276 files, 0 errors, 32
warnings, exit 0
, counted from --format json. The new test file is in that
population with 0/0. Invariance for the packages/* / apps/* lanes this
narrows away: eslint.config.js contains 0 occurrences of projectService,
parserOptions or project: (control: 10 occurrences of rules in the same
run), so no type-aware linting is enabled and this diff cannot move the verdict
on any file it did not touch — and it touches no file in those lanes.

Type-check is measured, not assumed: tsc -p tsconfig.scripts.json --listFiles
returns the new test file, so type-check:scripts really compiled it.


Generated by Claude Code

The date-face pin family asserts literal LOCAL-date faces (`Jul 4, 2024`,
`7/4/2024 7:00 am`) built from fixed UTC instants through local date parts,
and nothing pinned the runner's zone. Measured over the six files carrying
the family at 76573a1: green at UTC, 7 failures at Europe/Paris, 7 at
Asia/Shanghai, 31 at America/New_York, 33 at Etc/GMT+8 — green at exactly
one offset, invisible on CI (which is UTC) and paid entirely by
contributors elsewhere.

`vitest.config.mts` now sets `process.env.TZ = 'UTC'` in module scope, which
`apps/console/vitest.config.ts` inherits by importing it. The literal faces
stay: they are deliberate anti-tautology anchors, and deriving them from the
formatter under test would turn them into `formatDate(v) === formatDate(v)`.

`scripts/__tests__/vitest-timezone-pin-8366.test.ts` spawns a real vitest
under `TZ=Etc/GMT+8` and asserts the run still sees UTC, so deleting the
pin reds CI rather than only a contributor's laptop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

One re-run taken, and it is the only one this PR gets. domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-08T02:31Z.

Test (shard 2/4) failed on e1641f8e6 at 02:23:33Z. ⛔ Not this PR's, and the discrimination was taken rather than assumed:

FAIL packages/plugin-kanban/src/ObjectKanban.contractEnvelope-6839.test.tsx:134
     > still reads the contract's `data` member
     expected +0 to be 2          (676/677 files, 9022/9023 tests otherwise green)

That is the identical case and the identical numbers that removed PR #8462 from the merge queue at 01:10:28Z — a PR whose entire diff was two comment blocks — and it passed on the same base for PR #8461 in between, then passed for #8462 on re-queue. This PR's diff is a TZ='UTC' pin, a guard test and a changeset; the failing assertion counts kanban cards drawn from a { data: [...] } envelope, which contains no date. Controls in the same window: shard 2 passed on #8467, #8470 and #8480.

⇒ Filed as objectui#8493 (unlabelled, unassigned — the repair lands in plugin-kanban or @object-ui/core, ⛔ neither this lane's). Re-run of the failed jobs queued at 02:31Z; the first attempt returned 403 This workflow is already running and was retried once the run concluded.

⛔ Nothing about this branch changes for it: no rebase, no force-push, no edit. If the re-run is red on the same case, that is the second failure and this PR stops here rather than spending another cycle — the card would then be a live blocker rather than an intermittent one, and this seat would say so on objectui#8493 instead of re-running again.


Generated by Claude Code

@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ CORRECTION — this seat's "not this PR's" attribution no longer holds, and the PR stops here. domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-08T02:50Z.

The re-run failed on the same case with the same numbers: ObjectKanban.contractEnvelope-6839.test.tsx:134, expected +0 to be 2, 676/677 files and 9022/9023 tests otherwise green (job 101917709310). Per the commitment written above, ⛔ no second re-run: two failures on the same head is not intermittent behaviour for this head.

Why the earlier attribution was wrong, stated plainly

At 02:31Z this seat wrote that the failure was "⛔ not this PR's", on the strength of one occurrence and the argument that "the failing assertion counts kanban cards drawn from a { data: [...] } envelope, which contains no date." ⚠️ That argument was about the assertion, not about the render path or the fixture, and one failure was not enough to carry it. The reading now:

head / base diff Test (shard 2/4)
PR #8462's group, base c13d39e87 two comment blocks FAIL
PR #8461's group, same base a test file pass
PR #8462 re-queued, later base unchanged pass
PR #8486 e1641f8e6, base 76573a184 vitest.config.mts TZ pin FAIL
PR #8486 e1641f8e6, re-run, same head unchanged FAIL
PR #8487, same base 76573a184 docs pages + a pin re-declaration pass

⇒ At the same base, a PR that does not touch vitest.config.mts passes and this one fails twice. That points at this diff, and it is stronger evidence than the argument it displaces.

⭐ The likelier reading, and why it may make this PR more valuable rather than less

Two things can both be true: an intermittent failure exists independent of this branch (#8462 failed and #8461 passed on one base), and this diff converts it into a deterministic one. Pinning TZ='UTC' changes the ambient zone every test runs under. If ObjectKanban.contractEnvelope-6839.test.tsx is itself zone-sensitive, then:

  • ⛔ the pin did not break it — the pin exposed it, and the test was previously passing or failing according to the runner's zone, which is precisely the class objectui#8366 exists to close; and
  • the honest fix is in the kanban test, ⛔ not in the pin, and this PR is blocked on it rather than wrong.

⚠️ This seat has NOT measured that and will not assert it. The measurement that settles it is one run of that single file under TZ=UTC against the same file under the container's ambient zone, on this branch and on its base. That is dev work and is being dispatched.

What happens to this PR

⛔ Not closed, ⛔ not rebased, ⛔ not re-run again. It stays open at e1641f8e6 while the measurement is taken. If the kanban test proves zone-sensitive, this PR either grows the repair or is split, and objectui#8493 is re-scoped from "intermittent" to "zone-dependent, exposed by the pin". If it proves insensitive, the pin's interaction is the finding and this PR is the one that has to answer for it.

objectui#8493 is updated with the same correction — ⛔ its "non-deterministic" framing was written before the second failure and no longer describes the evidence.


Generated by Claude Code

@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

The mechanism this card declined to name, measured — and a re-scope: ⛔ not "intermittent, cause unknown". domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-08T03:10Z. Measured by the objectui#8366 dev on PR #8486's branch; routing reserved to this seat, which is why it arrives here rather than as a second card.

cardsThrough never waits on the rows it asserts about

Instrumented with a scratch probe (created, run, deleted; tree verified clean afterwards, git diff HEAD empty, HEAD unchanged):

case A   atMount                 { cards: 0, header: false }
         afterFindCalled         { cards: 0, header: false }
         afterAwaitFindPromise   { cards: 0, header: false }
         afterHeaderWait         { cards: 2, header: true }   <- the original's read point

cards() reads 0 at every explicit checkpoint the helper has. The rows only arrive during the trailing waitFor on the column header, as a side effect of an unrelated wait. The assertion has no margin because nothing in the helper ever waits on its own subject.

⚠️ And a correction to this test's own header comment (and to the dispatch brief that repeated it): the Negotiation header is described as a mount signal that "lands on every arm, refused or not." Measured, it is not — it is absent at mount, absent after find() is called, and absent after find's own promise is awaited. Header and cards become true/2 together, inside the same waitFor.

⭐ Why it is always the FIRST case, which is the part that makes it look random

case B (same sequence minus only the trailing header wait)
         cards already 2,  macrotaskTurnsUntilRows = 0

ObjectKanban gates its fetch on useSettledSchema, and once warm the definition is served from MetadataCache. ⇒ The file's second case pays no settle at all. Every CI failure observed today is the first it() in the file — the one paying the cold, uncached settle. That is a complete account of an order- and warmth-dependent assertion, needing no contribution from anything else.

Proposed patch shape, ⛔ not implemented here

Wait on the rows the case is about, per arm — ⛔ and without collapsing the negative records arm into a tautology: that arm must still assert zero cards after a settle signal not derived from the card count. ⚠️ A repair that waits on "cards > 0" would make the negative arm unfalsifiable, which is the failure mode this file's own header was written to prevent.

⛔ The repair lands in packages/plugin-kanban (or the @object-ui/core unwrap it routes through) — ⛔ not this lane's, and this seat neither claims nor grades it.

⚠️ Correction: this seat retracted an attribution that was RIGHT, and the retraction is now withdrawn

At 02:31Z this seat called the failure "⛔ not this PR's". At 02:50Z, after a second failure on the same head, it retracted that — on the reasoning that two failures on one head is a property of that head, and that PR #8487 passed shard 2 at the same base. ⇒ The retraction was wrong, and the original reading was right. Three legs settle it, and none of them is frequency:

  1. Shard 2 is byte-identical on base and branch — same 677 specs, same hash order, zero set difference either way. The file PR test(runner): pin the suite's timezone to UTC #8486 adds hashes to sorted index 2011, landing in shard 3, outside shard 2's range [677, 1354). ⇒ The diff cannot have reshuffled shard 2; the only delta inside it is one config line. Control on that arithmetic: the same reimplementation of vitest's BaseSequencer.shard predicts packages/cli/src/__tests__/cli-bin.test.ts into shard 2, and the CI log independently confirms it ran there.
  2. The CI runner is already UTC, derived from the log rather than assumed: vitest prints Start at in the runner's local zone; the failing job printed 02:30:23, and the same log gives run end 02:45:38Z with Duration 914.83s. Back-computing: 02:45:38 − 914.83s = 02:30:23.17. Equal to the second. ⇒ On a UTC host, process.env.TZ = 'UTC' is a semantic no-op — getTimezoneOffset, Intl…resolvedOptions().timeZone and the rendered en-US face are identical before and after. (Re-checked independently by this seat: the arithmetic holds.)
  3. The kanban file is not zone-sensitive at all — on the base, with no pin, it passes under TZ=UTC, Etc/GMT+8, America/New_York and Asia/Shanghai. It renders no dates.

The lesson this seat is carrying forward is not the one it wrote two hours ago. "Two failures on the same head is a property of that head" is insufficient: two failures are perfectly consistent with an external flake that hits a cold-path-sensitive first case twice. What settles attribution is whether the diff can reach the failing code — a structural question, answered here by shard membership — ⛔ not a frequency question. Both of this seat's earlier positions were held on evidence that could not decide it.

⚠️ Stated plainly because a negative result is a result: the dev could not reproduce the CI failure locally in any of four configurations — the file alone on the branch; the file on the base under four zones; a 202-file contention slice from the real shard-2 list including the 43-second cli-bin.test.ts; the file ×6 under 12 CPU hogs; and the failing case alone ×8 under 16 hogs. 0 failures in all of them. ⇒ The mechanism above is an account of how the assertion can lose, ⛔ not a reproduction.


Generated by Claude Code

@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Unblocked — pm:blockedpm:dispatched, and PR #8486's branch is updated onto the repaired main. domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R47, 2026-09-08T08:21Z.

The blocker is gone, measured on origin/main

PR #8533 merged at 07:58:29Z as 90a9aab32 (test(plugin-kanban): split the contractEnvelope-6839 waits by expected outcome, closing objectui#8532) — the same case that held this PR. ⭐ It is the correct repair, not just a green one: the file now carries an explicit outcome type, so the positive arms wait on the rows they assert about while the refusing arm still asserts zero after a settle signal ⛔ not derived from the card count.

⚠️ It also corrects the mechanism this seat relayed onto objectui#8493: the gate is React.lazy(() => import('./KanbanImpl')) behind a Suspense, ⛔ not useSettledSchema warmth. The header is absent until that chunk resolves. ⇒ This seat's relayed account was right about "the helper never waits on its own subject" and wrong about why the header lags. Recorded here because this card's readers were given that account.

What was done, in order, each verified

  1. pm:blockedpm:dispatched in one label write, read back and diffed against the union. The Blocked-by: #8493 line stays in the body as the historical record of why it waited — ⛔ removing it would erase the reason.
  2. PR test(runner): pin the suite's timezone to UTC #8486's branch updated onto the base via GitHub's own update-branch operation: head e1641f8e61aa257bd9, base now b8b0ac1d7. ⭐ That is a merge commit, ⛔ never a rebase and ⛔ never a force-push — anyone with this branch checked out keeps a valid checkout.

⚠️ Why an update rather than a re-run: Test (shard 2/4) was red on e1641f8e6 from before the repair, and a re-run replays the workflow at its pinned head sha ⇒ it would have re-measured the old tree and failed again. The branch had to move to see the fix.

What happens next, and the one thing that is NOT yet established

CI is re-running on 1aa257bd9. ⚠️ ⛔ This seat has not verified that the shard is green on this branch — only that the repair is on the base it now carries. If Test (shard 2/4) is still red here, that is a new fact about 90a9aab32, ⛔ not a residue of the old one, and it goes back to objectui#8493 with the run id rather than being re-rolled.

On green: flip ready → post-flip governed guard → auto-merge SQUASH → queue confirmed from the timeline event → landing probe on the TZ pin with a firing control and a nonsense control.

⚠️ Hot-file note for whoever touches this next. PR #8538 (card objectui#8500, in flight) also modifies vitest.config.mts. Measured: their hunks are ~295 lines apart — #8486 at @@ -7,6 +7,54 @@, #8538 at @@ -302,11 +302,38 @@ ⇒ ⛔ no textual conflict is predicted, but whichever lands second will need its branch updated the same way this one just was.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 8, 2026 08:50
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 389902e Sep 8, 2026
33 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-8366-date-face-timezone branch September 8, 2026 09:05
@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

LANDED — 389902ea9 on origin/main. domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R47, 2026-09-08T09:10Z. PR #8486 (card objectui#8366), merged first pass after the branch was updated onto the repaired base.

This PR sat pm:blocked for ~6 hours on a defect its diff could not reach, and the branch never changed to unblock it — only its base did. ⇒ The attribution written at 03:12Z held all the way through: structural, not statistical.

Landing probe, on a re-fetched origin/main (389902ea9)

probe expect read
process.env.TZ in vitest.config.mts > 0 2
'UTC' in the same file > 0 1
scripts/__tests__/vitest-timezone-pin-8366.test.ts present in the tree present ✔ (git ls-tree)
firing controldefineConfig in the same file, same command shape > 0 3
nonsense controlzzq-not-a-real-token-8366 0 0

⇒ The zero is a reading: the same command returns 3 on a term certainly present in the same file.

What is now on main

The suite's timezone is pinned to UTC at the runner level, with a spawn-based guard test that fails if the pin is removed — so the date-face family of assertions no longer depends on the ambient zone of whatever machine runs them.

⚠️ One thing this card's history is worth keeping for

This card is the first of three today whose PR was dequeued or held by ObjectKanban.contractEnvelope-6839.test.tsx — a case in a package none of the three diffs touches. The seat's first instinct on it was wrong and is on the record: a rule reading "two failures on the same head is a property of that head" was written, acted on, produced a retracted attribution on this very PR, and was then withdrawn. What replaced it — ⭐ attribution is structural: what settles ownership is whether the diff can reach the failing code — is the rule that then held for #8511, for #8538's Bundle Analysis red, and for this PR through six hours of being blocked.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants